;;; emaci.el --- Emacs and vi mode

;; Copyright (C) 2010 ahei

;; Author: ahei <ahei0802@gmail.com>
;; Keywords: emacs vi
;; URL: http://code.google.com/p/dea/source/browse/trunk/my-lisps/emaci.el
;; Time-stamp: <2010-04-13 10:23:58 Tuesday by ahei>

;; This  file is free  software; you  can redistribute  it and/or
;; modify it under the terms of the GNU General Public License as
;; published by  the Free Software Foundation;  either version 3,
;; or (at your option) any later version.

;; This file is  distributed in the hope that  it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR  A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You  should have  received a  copy of  the GNU  General Public
;; License along with  GNU Emacs; see the file  COPYING.  If not,
;; write  to  the Free  Software  Foundation,  Inc., 51  Franklin
;; Street, Fifth Floor, Boston, MA 02110-1301, USA.

;;; Commentary:
;;
;; emaci-mode make  you browse and  edit your file  quickly.  Use
;; it, you  can browse file like  vi, but more  quickly, and when
;; you  browe file, you  can also  edit your  file use  Emacs key
;; bindings.  It's name  "emaci" is combine of EMACs  and vI. Its
;; screenshots is http://emacser.com/screenshots/emaci.png . This
;; package          is         depend          on         package
;; http://code.google.com/p/dea/source/browse/trunk/my-lisps/ahei-misc.el
;; and                                                     package
;; http://code.google.com/p/dea/source/browse/trunk/my-lisps/eval-after-load.el
;; . For more details, see article http://emacser.com/emaci.htm

;;; Installation:
;;
;; Copy emaci.el to your load-path and add to your .emacs:
;;
;; (require 'emaci)

;;; History:
;;
;; 2010-3-21
;;      * initial version 1.0.

;;; Code:

(require 'misc)
(require 'ahei-misc)
(require 'eval-after-load)
  
(defgroup emaci nil
  "Minor mode for browse and edit file more quickly."
  :prefix "emaci-")

;;;###autoload
(defcustom emaci-mode-line-format (propertize "Emaci" 'face 'emaci-mode-line-face)
  "Mode line format of function `emaci-mode'."
  :type 'string
  :group 'emaci)

;;;###autoload
(defcustom emaci-maps-to-bind-brief-keys
  `(view-mode-map
    Man-mode-map
    apropos-mode-map
    completion-list-mode-map
    log-view-mode-map
    compilation-mode-map
    diff-mode-map
    help-mode-map
    ,(if (>= emacs-major-version 21) 'grep-mode-map)
    color-theme-mode-map)
  "List used for `emaci-bind-modes-keys'.

Element of this list either a list whose first element is load file,
and second element is map, or a string which means load file, or a map."
  :group 'emaci)

;;;###autoload
(defcustom emaci-brief-key-defs
  `(("h" backward-char)
    ("l" forward-char)
    ("j" next-line)
    ("k" previous-line)
    ("J" roll-down)
    ("K" roll-up)
    ("b" backward-word)
    ("w" am-forward-word-or-to-word)
    ("o" other-window)
    ("G" end-of-buffer)
    ("a" move-beginning-of-line)
    ("e" move-end-of-line))
  "Key pairs used bind in modes `emaci-maps-to-bind-brief-keys'.

Each element of this variable is two-elements list, and first
element is key like argument of `kbd', and second element is command."
  :type 'list
  :group 'emaci)

(defvar emaci-mode-map nil "Keymap for function `emaci-mode'.")

(defvar emaci-read-only nil "Readonly before enter in function `emaci-mode' or not.")
(make-variable-buffer-local 'emaci-read-only)

(defface emaci-mode-line-face
  '((((type tty pc)) :bold t :foreground "red" :background "white")
    (t (:background "red" :foreground "white")))
  "Face used highlight `emaci-mode-line-format'.")

;; must do this
(put 'emaci-mode-line-format 'risky-local-variable t)

(unless emaci-mode-map
  (setq emaci-mode-map (make-sparse-keymap))
  (setq minor-mode-alist
        (append
         `((emaci-mode " ") (emaci-mode ,emaci-mode-line-format))
         (delq (assq 'emaci-mode minor-mode-alist) minor-mode-alist))))

(eal-define-keys
 'emaci-mode-map
 `(("a"   move-beginning-of-line)
   ("e"   move-end-of-line)
   ("j"   next-line)
   ("k"   previous-line)
   ("h"   backward-char)
   ("l"   forward-char)
   ("n"   emaci-n)
   ("p"   previous-line)
   ("u"   View-scroll-half-page-backward)
   ("SPC" scroll-up)
   ("d"   View-scroll-half-page-forward)
   ("g"   beginning-of-buffer)
   ("<"   beginning-of-buffer)
   (">"   end-of-buffer)
   ("J"   emaci-roll-down)
   ("K"   emaci-roll-up)
   ("w"   am-forward-word-or-to-word)
   ("b"   backward-word)
   ("x"   delete-char)
   ("I"   emaci-bol-and-quit)
   ("A"   emaci-eol-and-quit)
   ("o"   other-window)
   ("O"   emaci-newline-and-quit)
   ("m"   back-to-indentation)
   ("q"   emaci-q)
   ("Q"   delete-current-window)
   ("1"   delete-other-windows)
   ("B"   eval-buffer)))

(am-def-active-fun edebug-active)

;;;###autoload
(defun emaci-n ()
  "Command bind to key n."
  (interactive)
  (if (edebug-active)
      (edebug-next-mode)
    (if (equal major-mode 'gud-mode)
        (call-interactively 'gud-next)
      (call-interactively 'next-line))))

;;;###autoload
(defun emaci-q ()
  "Command bind to key q."
  (interactive)
  (if (edebug-active)
      (top-level)
    (emaci-mode-off)))

;;;###autoload
(defun emaci-bol-and-quit ()
    "First exit function `emaci-mode' and then call `back-to-indentation', like I command in vi."
    (interactive)
    (emaci-mode-off)
    (back-to-indentation))

;;;###autoload
(defun emaci-eol-and-quit ()
  "First exit function `emaci-mode' and then call `end-of-line', like A command in vi."
  (interactive)
  (emaci-mode-off)
  (end-of-line))

;;;###autoload
(defun emaci-newline-and-quit ()
  "First exit function `emaci-mode' and then call `newline-and-indent', like o command in vi."
  (interactive)
  (emaci-mode-off)
  (end-of-line)
  (newline-and-indent))

;;;###autoload
(defun emaci-roll-down (&optional n)
  "Simulate roll down lines N."
  (interactive "P")
  (if (null n) (setq n 7))
  (next-line n))

;;;###autoload
(defun emaci-roll-up (&optional n)
  "Simulate roll up lines N."
  (interactive "P")
  (if (null n) (setq n 7))
  (previous-line n))

(add-hook 'find-file-hook 'emaci-exist-file)

;; when open exist file automatically enter `emaci-mode', but if file
;; does not exist, do not enter it.
;;;###autoload
(defun emaci-exist-file ()
  "Only when variable `buffer-file-name' is exist, enter function `emaci-mode'."
  (when (file-exists-p (buffer-file-name))
    (emaci-mode-on)))

;;;###autoload
(defun emaci-bind-brief-keys ()
  "Bind brief keys."
  (interactive)
  (eal-define-keys emaci-maps-to-bind-brief-keys emaci-brief-key-defs))

;;;###autoload
(define-minor-mode emaci-mode
  "Toggle `emaci-mode'.

  \\{emaci-mode-map}
Entry to this mode calls the value of `emaci-mode-hook'
if that value is non-nil.  \\<emaci-mode-map>"
  :group 'emaci
  (if emaci-mode
      (progn
        (setq emaci-read-only buffer-read-only)
        (setq buffer-read-only nil))
    (setq buffer-read-only emaci-read-only)))

;;;###autoload
(defun emaci-mode-on ()
  "Turn on function `emaci-mode'."
  (interactive)
  (emaci-mode 1))

;;;###autoload
(defun emaci-mode-off ()
  "Turn off function `emaci-mode'."
  (interactive)
  (emaci-mode -1))

(emaci-bind-brief-keys)

(provide 'emaci)

;;; emaci.el ends here
